package com.axonactive.yolo.service.api;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dtotemp.Carrier;
import com.axonactive.yolo.dtotemp.FlightClass;
import com.axonactive.yolo.dtotemp.Location;
import com.axonactive.yolo.dtotemp.Price;
import com.axonactive.yolo.dtotemp.Schedule;
import com.axonactive.yolo.dtotemp.WegoFlight;
import com.axonactive.yolo.service.internal.FlightRequester;
import com.axonactive.yolo.util.WegoUtilities;

public class WegoAPI extends FlightRequester {

	private static final long TIME_OUT = 5000;
	private String API_KEY = "f931406558d22da33595";
	private String ts_code = "1bcc1";
//	private String format = "json";
	private Map<Integer, APIKEY> keys;
	private static final Logger LOGGER = Logger.getLogger(WegoAPI.class);


	public WegoAPI() {
		this.cabinClass="Economy";
		keys = new Hashtable<Integer, APIKEY>();
		keys.put(1, new APIKEY("ef7806be1c655b54d9a6", "0b755"));
		keys.put(0, new APIKEY("f931406558d22da33595", "1bcc1"));
	}

	@Override
	public List<WegoFlight> processResponseWego(String response) {
		try {
			JSONObject object = new JSONObject(response);
			if (object.has("response") == false) {
				return null;
			}
			JSONObject responseObj = object.getJSONObject("response");
			JSONArray itineraries = responseObj.getJSONArray("itineraries");
			List<WegoFlight> flights = new ArrayList<WegoFlight>();
			for (int i = 0; i < itineraries.length() - 1; i++) {
				WegoFlight flight = new WegoFlight();
				Price price = new Price();
				Schedule schedule = new Schedule();
				List<Carrier> carriers = new ArrayList<Carrier>();
				List<Location> locations = new ArrayList<Location>();
				List<FlightClass> flightClasses = new ArrayList<FlightClass>();
				JSONObject temp = new JSONObject();
				JSONArray temp_array = new JSONArray();
				JSONObject flightObj = itineraries.getJSONObject(i);
				String[] str_array;
				// Read list of carriers
				temp_array = flightObj.getJSONArray("carrier");
				for (int j = 0; j < temp_array.length(); j++) {
					Carrier carrier = new Carrier();
					JSONObject carrierObj = temp_array.getJSONObject(j);
					carrier.setCode(carrierObj.getString("code"));
					carrier.setName(carrierObj.getString("name"));
					carriers.add(carrier);
				}
				flight.setCarriers(carriers);

				// Read list of locations
				temp_array = flightObj.getJSONArray("location");
				for (int j = 0; j < temp_array.length(); j++) {
					Location location = new Location();
					JSONObject carrierObj = temp_array.getJSONObject(j);
					location.setCode(carrierObj.getString("code"));
					location.setName(carrierObj.getString("name"));
					locations.add(location);
				}
				flight.setLocations(locations);

				// Read provider Id
				flight.setProviderId(flightObj.getString("providerId"));

				// Read originCountryCode
				flight.setOriginCountryCode(flightObj
						.getString("originCountryCode"));

				// Read destinationCountryCode
				flight.setDestinationCountryCode(flightObj
						.getString("destinationCountryCode"));

				// Read price
				temp = flightObj.getJSONObject("price");
				price.setCurrencyCode(temp.getString("currencyCode"));
				price.setAmount(temp.getString("amount"));
				flight.setPrice(price);

				// Read outboundInfo
				temp = flightObj.getJSONObject("outboundInfo");
				// + read airports
				temp_array = temp.getJSONArray("airports");
				str_array = new String[temp_array.length()];
				for (int j = 0; j < temp_array.length(); j++) {
					str_array[j] = temp_array.getString(j);
				}
				schedule.setAirports(str_array);
				// + read airports expanded
				temp_array = temp.getJSONArray("airportsExpanded");
				str_array = new String[temp_array.length()];
				for (int j = 0; j < temp_array.length(); j++) {
					str_array[j] = temp_array.getString(j);
				}
				schedule.setAirportsExpanded(str_array);
				// + read flightClasses
				temp_array = temp.getJSONArray("flightClasses");
				for (int j = 0; j < temp_array.length(); j++) {
					FlightClass flightClass = new FlightClass();
					JSONObject flightClassObj = temp_array.getJSONObject(j);
					flightClass
							.setFareClass(flightClassObj.getInt("fareClass"));
					try {
						flightClass.setName(flightClassObj.getString("name"));
					} catch (JSONException e) {
						LOGGER.error("JSONException",e);
						flightClass.setName("");
					}
					flightClasses.add(flightClass);
				}
				schedule.setFlightClasses(flightClasses);
				// + read numStops
				schedule.setNumStops(temp.getInt("numStops"));
				// + read localArrivalTimeStr
				schedule.setLocalArrivalTimeStr(temp
						.getString("localArrivalTimeStr"));
				// + read localDepartureTimeStr
				schedule.setLocalDepartureTimeStr(temp
						.getString("localDepartureTimeStr"));
				// + read depatureTime
				schedule.setArrivalTime(temp.getLong("arrivalTime"));
				// + read arrivalTime
				schedule.setDepartureTime(temp.getLong("departureTime"));
				// + read durationInMin
				schedule.setDurationInMin(temp.getInt("durationInMin"));

				// + read flight number
				temp_array = temp.getJSONArray("flightNumbers");
				str_array = new String[temp_array.length()];
				for (int j = 0; j < temp_array.length(); j++) {
					str_array[j] = temp_array.getString(j);
				}
				schedule.setFlightNumbers(str_array);
				// add schedule into flight
				flight.setOutboundInfo(schedule);

				// Read inboundInfo
				// + check tripTye is roundStrip, we will get inboundInfo
				if (this.tripType.toLowerCase().equals("roundtrip")
						&& isJSONOBject(flightObj, "inboundInfo") == true) {
					Schedule inboundInfo = new Schedule();
					flightClasses = new ArrayList<FlightClass>();
					temp = flightObj.getJSONObject("inboundInfo");
					// + read airports
					temp_array = temp.getJSONArray("airports");
					str_array = new String[temp_array.length()];
					for (int j = 0; j < temp_array.length(); j++) {
						str_array[j] = temp_array.getString(j);
					}
					inboundInfo.setAirports(str_array);
					// + read airports expanded
					temp_array = temp.getJSONArray("airportsExpanded");
					str_array = new String[temp_array.length()];
					for (int j = 0; j < temp_array.length(); j++) {
						str_array[j] = temp_array.getString(j);
					}
					inboundInfo.setAirportsExpanded(str_array);
					// + read flightClasses
					temp_array = temp.getJSONArray("flightClasses");
					for (int j = 0; j < temp_array.length(); j++) {
						FlightClass flightClass = new FlightClass();
						JSONObject flightClassObj = temp_array.getJSONObject(j);
						flightClass.setFareClass(flightClassObj
								.getInt("fareClass"));
						try {
							flightClass.setName(flightClassObj
									.getString("name"));
						} catch (JSONException e) {
							LOGGER.error("JSONException",e);
							flightClass.setName("");
						}
						flightClasses.add(flightClass);
					}
					inboundInfo.setFlightClasses(flightClasses);
					// + read numStops
					inboundInfo.setNumStops(temp.getInt("numStops"));
					// + read localArrivalTimeStr
					inboundInfo.setLocalArrivalTimeStr(temp
							.getString("localArrivalTimeStr"));
					// + read localDepartureTimeStr
					inboundInfo.setLocalDepartureTimeStr(temp
							.getString("localDepartureTimeStr"));
					// + read depatureTime
					inboundInfo.setArrivalTime(temp.getLong("arrivalTime"));
					// + read arrivalTime
					inboundInfo.setDepartureTime(temp.getLong("departureTime"));
					// + read durationInMin
					inboundInfo.setDurationInMin(temp.getInt("durationInMin"));
					// + read flight number
					temp_array = temp.getJSONArray("flightNumbers");
					str_array = new String[temp_array.length()];
					for (int j = 0; j < temp_array.length(); j++) {
						str_array[j] = temp_array.getString(j);
					}
					inboundInfo.setFlightNumbers(str_array);
					// add schedule into flight
					flight.setInboundInfo(inboundInfo);
				}

				// add flight into list of flights
				flights.add(flight);
			}
			return flights;
		}catch (JSONException je) {
			LOGGER.error("JSONException",je);
		}
		return null;
	}

	@Override
	public String sendRequest() {
		String start_search = "http://www.wego.com/api/flights/startSearch.html?format=json&fromLocation="
				+ this.departureAirportCode
				+ "&toLocation="
				+ this.arrivalAirportCode
				+ "&tripType="
				+ this.tripType
				+ "&cabinClass="
				+ this.cabinClass
				+ "+&outboundDate="
				+ this.outboundDate
				+ "&numAdults="
				+ this.numAdults
				+ "&numChildren=" + this.numChildren + "";
		if (this.inboundDate != "") {
			start_search += "&inboundDate=" + this.inboundDate;
		}
		try {
			String start_search_response = null;

			for (int i = 0; i < keys.size(); i++) {
				String new_start_search = start_search;
				this.API_KEY = keys.get(i).getAPI_KEY();
				this.ts_code = keys.get(i).getTs_code();
				new_start_search += "&apiKey=" + this.API_KEY + "&ts_code="
						+ this.ts_code + "";
				start_search_response = call(new_start_search);
				if (isExceed(start_search_response) == false) {
					break;
				}
			}
			String instanceId = new JSONObject(start_search_response)
					.getJSONObject("request").getString("instanceId");
			Thread.sleep(TIME_OUT);
			String pull = "http://www.wego.com/api/flights/pull.html?format=json&apiKey="
					+ API_KEY
					+ "&instanceId="
					+ instanceId
					+ "&rand=1"
					+ "&currency=" + this.currencyCode;
			String pull_response = call(pull);
			return pull_response;
		} catch (IOException io_ex) {
			LOGGER.error("IOException", io_ex);
		} catch (JSONException js_ex) {
			LOGGER.error("JSONException", js_ex);
		} catch (InterruptedException ie) {
			LOGGER.error("InterruptedException", ie);

		}

		return "";

	}

	private static String call(String url) throws IOException {
		URL requestUrl = new URL(url);
		URLConnection con = requestUrl.openConnection();
		BufferedReader rd = new BufferedReader(new InputStreamReader(
				con.getInputStream()));
		StringBuilder sb = new StringBuilder();
		int cp;
		try {
			while ((cp = rd.read()) != -1) {
				sb.append((char) cp);
			}
		} catch (Exception e) {
			LOGGER.error("Exception",e);
		}
		String json = sb.toString();
		LOGGER.info("Response from wego: " + json);
		return json;
	}

	private boolean isExceed(String response) {
		try {
			String msg = new JSONObject(response).getString("error");
		} catch (JSONException js_ex) {
			LOGGER.error("JSONException", js_ex);
			return false;
		}
		return true;
	}

	private boolean isJSONOBject(JSONObject parent, String name) {
		try {
			parent.getJSONObject(name);
			return true;
		} catch (JSONException js_ex) {
			LOGGER.error("JSONException", js_ex);
			return false;
		}
	}

	@Override
	public List<Flight> processResponse(String response) {
		// Get list of WegoFlight and convert it to Flight objects
		List<Flight> flights = new ArrayList<Flight>();
		List<WegoFlight> wegoFlights = processResponseWego(response);
		if (wegoFlights == null) {
			return null;
		} else {
			for (Iterator<WegoFlight> iterator = wegoFlights.iterator(); iterator.hasNext();) {
				WegoFlight wegoFlight = (WegoFlight) iterator.next();
				flights.add(WegoUtilities.convertToFlight(wegoFlight));
			}
			return flights;
		}
	}
}

class APIKEY {
	private String API_KEY;
	private String ts_code;

	public APIKEY(String API_KEY, String ts_code) {
		this.API_KEY = API_KEY;
		this.ts_code = ts_code;
	}

	public String getAPI_KEY() {
		return API_KEY;
	}

	public void setAPI_KEY(String aPI_KEY) {
		API_KEY = aPI_KEY;
	}

	public String getTs_code() {
		return ts_code;
	}

	public void setTs_code(String ts_code) {
		this.ts_code = ts_code;
	}
}